home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 1.iso / ARGONET / PD / FILER / X-FILES.ZIP / 057 / !X-Files / c / cache next >
Text File  |  1997-01-22  |  23KB  |  962 lines

  1. /* cache.c */
  2.  
  3. /*#define NOCACHE*/
  4. #define NOWRITEBEHIND
  5.  
  6. #include "chunks.h"
  7. #include "debug.h"
  8. #include "module.h"
  9. #include "swis.h"
  10. #include <stdlib.h>
  11. #include <string.h>
  12.  
  13. static int xFiles__timer;
  14.  
  15. #define xFiles_CACHECHUNKSIZE     1024
  16. #define xFiles_OK2CACHE           4096
  17.  
  18. #define roundDown(x) ((x) & ~(xFiles_CACHECHUNKSIZE-1))
  19. #define roundUp(x)   roundDown((x) + xFiles_CACHECHUNKSIZE - 1)
  20.  
  21. typedef struct
  22. {
  23.    int prev, next;    /* Index into the array of cache chunks or -1 for NULL */
  24.    unsigned owner;    /* (unsigned) pInfo for this chunk or 0 if unused      */
  25.    unsigned offset;   /* Offset in file where it started                     */
  26.    char     *buffer;
  27.    BOOL     dirty;
  28.  
  29. } xFiles_cacheItem;
  30.  
  31. static xFiles_cacheItem *cache;
  32. static char             *buffer;
  33. static int               cacheHead, cacheTail;
  34. static int               cacheSize, cacheUsed; /* elements that is */
  35.  
  36. void *xFiles_windowBuffer;    /* shared by all and sundry */
  37. int   xFiles_windowBufferSize;
  38. static BOOL  xFiles__bufferClaimed;
  39.  
  40. #define _max(x, y) ((x) > (y) ? (x) : (y))
  41. #define _min(x, y) ((x) < (y) ? (x) : (y))
  42.  
  43. static void xFiles__disposeCache(void)
  44. {
  45.    if (cache)
  46.    {
  47.       free(cache);
  48.       cache = NULL;
  49.    }
  50.  
  51.    if (buffer)
  52.    {
  53.       free(buffer);
  54.       buffer = NULL;
  55.    }
  56. }
  57.  
  58. void xFiles_Tidy(void)
  59. {
  60.    if (xFiles_windowBuffer)
  61.    {
  62.       free(xFiles_windowBuffer);
  63.       xFiles_windowBuffer = NULL;
  64.    }
  65.  
  66.    xFiles__disposeCache();
  67. }
  68.  
  69. _kernel_oserror *xFiles_releaseBuffer(void)
  70. {
  71.    xFiles__bufferClaimed = FALSE;
  72.  
  73.    return NULL;
  74. }
  75.  
  76. _kernel_oserror *xFiles_claimBuffer(int notBiggerThan)
  77. {
  78.    (void) notBiggerThan;
  79.  
  80.    if (xFiles__bufferClaimed)
  81.       return &xFiles_BufferInUse;
  82.  
  83.    xFiles__bufferClaimed = TRUE;
  84.  
  85.    if (xFiles_windowBuffer)
  86.       return NULL;
  87.  
  88.    if (xFiles_windowBuffer = malloc(xFiles_WINDOWSIZE), !xFiles_windowBuffer)
  89.       return &xFiles_NoMemory;
  90.  
  91.    xFiles_windowBufferSize = xFiles_WINDOWSIZE;  
  92.  
  93.    return NULL;
  94. }
  95.  
  96. static void xFiles_RemoveCacheItem(int item)
  97. {
  98.    if (cache[item].prev != -1)
  99.       cache[cache[item].prev].next = cache[item].next;
  100.    else
  101.       cacheHead = cache[item].next;
  102.  
  103.    if (cache[item].next != -1)
  104.       cache[cache[item].next].prev = cache[item].prev;
  105.    else
  106.       cacheTail = cache[item].prev;
  107.  
  108.    cache[item].next =
  109.    cache[item].prev = -1;
  110. }
  111.  
  112. static void xFiles_CacheAddAtHead(int item)
  113. {
  114.    if (cacheHead != -1)
  115.       cache[cacheHead].prev = item;
  116.    else
  117.       cacheTail = item;
  118.  
  119.    cache[item].prev = -1;
  120.    cache[item].next = cacheHead;
  121.    cacheHead = item;
  122. }
  123.  
  124. static void xFiles_CacheAddAtTail(int item)
  125. {
  126.    if (cacheTail != -1)
  127.       cache[cacheTail].next = item;
  128.    else
  129.       cacheHead = item;
  130.  
  131.    cache[item].next = -1;
  132.    cache[item].prev = cacheTail;
  133.    cacheTail = item;
  134. }
  135.  
  136. _kernel_oserror *xFiles_getLength(xFiles_info *pInfo, unsigned *pLength)
  137. {
  138.    _kernel_swi_regs regs;
  139.    _kernel_oserror *err;
  140.  
  141.    if (pInfo->fileSize == (unsigned) -1)
  142.    {
  143.       regs.r[0] = 2;
  144.       regs.r[1] = pInfo->fileHandle;
  145.    
  146.       if (err = _kernel_swi(OS_Args, ®s, ®s), err)
  147.          return err;
  148.    
  149.       pInfo->fileSize = (unsigned) regs.r[2];
  150.    }
  151.  
  152.    if (pLength) *pLength = pInfo->fileSize;
  153.    
  154.    return NULL;
  155. }
  156.  
  157. _kernel_oserror *xFiles_setLength(xFiles_info *pInfo, unsigned Length)
  158. {
  159.    _kernel_swi_regs regs;
  160.    _kernel_oserror *err;
  161.    int i;
  162.    unsigned owner = (unsigned) pInfo;
  163.    unsigned oldLength;
  164.  
  165.    /*TRACE("xFiles_setLength(%p, %08x)\n", pInfo, Length);*/
  166.  
  167.    if (err = xFiles_getLength(pInfo, &oldLength), err)
  168.       return err;
  169.  
  170.    /* If the file is shrinking we need to discard any items which are cached
  171.     * for the bit of file which no longer exists, otherwise when the file
  172.     * grows again these bits will be mistaken for valid bits of file.
  173.     */   
  174.  
  175.    if (Length < oldLength)
  176.    {
  177.       for (i = 0; i < cacheUsed; i++)
  178.       {
  179.          if (cache[i].owner == owner && cache[i].offset >= Length)
  180.          {
  181.             cache[i].offset = 0xFFFFFFFF;
  182.             cache[i].dirty  = FALSE;
  183.             xFiles_RemoveCacheItem(i);
  184.             xFiles_CacheAddAtTail(i);
  185.          }
  186.  
  187.          if (cache[i].offset == 0xFFFFFFFF)
  188.             cache[i].owner  = i > 0 ? cache[i-1].owner + 1 : 0;
  189.       }
  190.    }
  191.    
  192.    regs.r[0] = 3;
  193.    regs.r[1] = pInfo->fileHandle;
  194.    regs.r[2] = (int) Length;
  195.  
  196.    if (err = _kernel_swi(OS_Args, ®s, ®s), err)
  197.       return err;
  198.  
  199.    pInfo->fileSize = Length;
  200.    return NULL;
  201. }
  202.  
  203. /* Read a glob of the file into the specified buffer.
  204.  * This is about as primitive as it gets.
  205.  */
  206.  
  207. static _kernel_oserror *xFiles_rawRead(xFiles_info *pInfo, void *pBuffer, unsigned pos, unsigned size)
  208. {
  209.    _kernel_swi_regs regs;
  210.    _kernel_oserror *err;
  211.  
  212.    /*TRACE("  xFiles_rawRead(%p, %p, %08x, %08x)\n", pInfo, pBuffer, pos, size);*/
  213.  
  214.    regs.r[0] = 3;  /* read bytes given pointer */
  215.    regs.r[1] = pInfo->fileHandle;
  216.    regs.r[2] = (int) pBuffer;
  217.    regs.r[3] = size;
  218.    regs.r[4] = pos;
  219.  
  220.    if (err = _kernel_swi(OS_GBPB, ®s, ®s), err)
  221.       return err;
  222.  
  223.    if (regs.r[3] > 0)
  224.    {
  225.       TRACE("*** %d bytes not read\n", regs.r[3]);
  226.    }
  227.  
  228.    return NULL;
  229. }
  230.  
  231. static _kernel_oserror *xFiles_rawWrite(xFiles_info *pInfo, void *pBuffer, unsigned pos, unsigned size)
  232. {
  233.    _kernel_swi_regs regs;
  234.  
  235.    /*TRACE("  xFiles_rawWrite(%p, %p, %08x, %08x)\n", pInfo, pBuffer, pos, size);*/
  236.  
  237.    regs.r[0] = 1;  /* write bytes given pointer */
  238.    regs.r[1] = pInfo->fileHandle;
  239.    regs.r[2] = (int) pBuffer;
  240.    regs.r[3] = size;
  241.    regs.r[4] = pos;
  242.  
  243.    return _kernel_swi(OS_GBPB, ®s, ®s);
  244. }
  245.  
  246. static void xFiles_EmptyCache()
  247. {
  248.    int i;
  249.    char *bp;
  250.  
  251.    cacheHead =
  252.    cacheTail = -1;
  253.  
  254.    /* Offsets within the buffer are statically associated with particular
  255.     * index entries, so this relationship is established here.
  256.     */
  257.     
  258.    for (i = 0, bp = buffer; i < cacheSize; i++, bp += xFiles_CACHECHUNKSIZE)
  259.    {
  260.       cache[i].prev =
  261.       cache[i].next = -1;
  262.       cache[i].owner = 0;
  263.       cache[i].offset = 0; 
  264.       cache[i].buffer = bp;
  265.       cache[i].dirty  = FALSE;
  266.    }
  267.  
  268.    cacheUsed = 0;
  269. }
  270.  
  271. static int xFiles_CacheCmp(unsigned owner, unsigned offset, int item)
  272. {
  273.    return (owner == cache[item].owner) ? (offset - cache[item].offset)
  274.                                        : (owner  - cache[item].owner);
  275. }
  276.  
  277. static int xFiles_CacheLookup(xFiles_info *pInfo, unsigned offset)
  278. {
  279.    unsigned owner = (unsigned) pInfo;
  280.    int lo, hi, mid, cmp;
  281.  
  282.    for (lo = 0, hi = cacheUsed-1; lo <= hi; )
  283.    {
  284.       mid = (lo + hi) / 2;
  285.       cmp = xFiles_CacheCmp(owner, offset, mid);
  286.       
  287.       if (cmp < 0)
  288.          hi = mid - 1;
  289.       else if (cmp > 0)
  290.          lo = mid + 1;
  291.       else
  292.          return mid;
  293.    }
  294.  
  295.    return -1;
  296. }
  297.  
  298.  
  299. _kernel_oserror *xFiles_InitCache(unsigned size)
  300. {
  301. #ifdef NOCACHE
  302.    (void) size;
  303. #else
  304.    cacheSize = size / xFiles_CACHECHUNKSIZE;
  305.  
  306.    xFiles__disposeCache();
  307.  
  308.    xFiles__timer = 0;
  309.  
  310.    if (cacheSize == 0)
  311.    {
  312.       cache = NULL;
  313.       return NULL;
  314.    }
  315.  
  316.    if (cache = malloc(sizeof(xFiles_cacheItem) * cacheSize), !cache)
  317.       goto fail;
  318.  
  319.    if (buffer = malloc(xFiles_CACHECHUNKSIZE * cacheSize), !buffer)
  320.       goto fail;
  321.  
  322.    xFiles_EmptyCache();
  323.    
  324.    return NULL;
  325.  
  326. fail:
  327.    xFiles__disposeCache();
  328.    cacheSize = 0;
  329.    TRACE("Not enough space for cache; running with cache disabled");
  330. #endif
  331.    return NULL;
  332. }
  333.  
  334. _kernel_oserror *xFiles_FlushFileInfo(xFiles_info *pInfo)
  335. {
  336.    int i;
  337.    unsigned owner = (unsigned) pInfo;
  338.    _kernel_oserror *err;
  339.  
  340.    if (err = xFiles_Flush(), err)
  341.       return err;
  342.  
  343.    for (i = 0; i < cacheUsed; i++)
  344.    {
  345.       if (cache[i].owner == owner)
  346.       {
  347.          cache[i].offset = 0xFFFFFFFF;
  348.          cache[i].dirty  = FALSE;
  349.          xFiles_RemoveCacheItem(i);
  350.          xFiles_CacheAddAtTail(i);
  351.       }
  352.  
  353.       if (cache[i].offset == 0xFFFFFFFF)
  354.          cache[i].owner = i > 0 ? cache[i-1].owner + 1 : 0;
  355.    }
  356.    
  357.    return NULL;
  358. }
  359.  
  360. static void xFiles_AdjustCache(int item, int by)
  361. {
  362.    int i;
  363.  
  364.    ASSERT(item != -1);
  365.  
  366.    for (i = 0; i < cacheUsed; i++)
  367.    {
  368.       ASSERT(by > 0 || cache[i].prev != item);
  369.       ASSERT(by > 0 || cache[i].next != item);
  370.  
  371.       if (cache[i].prev >= item)
  372.          cache[i].prev += by;
  373.       if (cache[i].next >= item)
  374.          cache[i].next += by;
  375.    }
  376.  
  377.    ASSERT(by > 0 || cacheHead != item);
  378.    ASSERT(by > 0 || cacheTail != item);
  379.  
  380.    if (cacheHead >= item)
  381.       cacheHead += by;
  382.    if (cacheTail >= item)
  383.       cacheTail += by;
  384. }
  385.  
  386. static void xFiles_CacheCheck(void)
  387. {
  388.    int prev, i, watchDog;
  389.    int broken = 0;
  390.    xFiles_info *pInfo;
  391.  
  392.    if (!cache)
  393.       return;
  394.  
  395.    if (cacheUsed > cacheSize)
  396.       goto trash;
  397.  
  398.    prev = -1;
  399.    i = cacheHead;
  400.    watchDog = cacheUsed+1;
  401.  
  402.    while (watchDog > 0 && i != -1)
  403.    {
  404.       if (cache[i].prev != prev)
  405.       {
  406.          TRACE("Cache back link is %d (should be %d)\n", cache[i].prev, prev);
  407.          broken++;
  408.       }
  409.  
  410.       prev = i;
  411.       i = cache[i].next;
  412.       watchDog--;
  413.    }
  414.  
  415.    if (watchDog == 0)
  416.    {
  417.       TRACE("Loop in cache links\n");
  418.       broken++;
  419.    }
  420.    else if (cacheTail != prev)
  421.    {
  422.       TRACE("cacheTail should is %d (should be %d)\n", cacheTail, prev);
  423.       broken++;
  424.    }
  425.  
  426.    for (i = 0; i < cacheUsed-1; i++)
  427.    {
  428.       int cmp = xFiles_CacheCmp(cache[i].owner, cache[i].offset, i+1);
  429.  
  430.       if (cmp >= 0)
  431.       {
  432.          TRACE("Cache items are not ordered/unique\n");
  433.          broken++;
  434.          break;
  435.       }
  436.  
  437.       pInfo = (xFiles_info *) cache[i].owner;
  438.       if (cache[i].offset != 0xFFFFFFFF && cache[i].offset >= pInfo->fileSize)
  439.       {
  440.          TRACE("Cache item outside file's length (offset = %08x, fileSize = %08x)\n",
  441.                       cache[i].offset, pInfo->fileSize);
  442.          broken++;
  443.          break;
  444.       }
  445.    }
  446.  
  447.    if (broken == 0)
  448.       return;
  449.  
  450. trash:
  451.    TRACE("Cache is broken: discarding it\n");
  452.  
  453.    TRACE("Head: %d, Tail: %d, Size: %d, Used: %d\n", cacheHead, cacheTail, cacheSize, cacheUsed);
  454.    for (i = 0; i < _min(cacheUsed, cacheSize); i++)
  455.    {
  456.       TRACE("%3d: %08x, %08x, %3d, %3d\n",
  457.                   i, cache[i].owner, cache[i].offset, cache[i].prev, cache[i].next);
  458.    }
  459.  
  460.    cacheUsed = 0;
  461.    cacheHead = cacheTail = -1;
  462. }
  463.  
  464. /* Ensure the chunk with the specified offset is in the cache, returning it's index.
  465.  * If the chunk isn't in the cache it will be loaded from the file replacing the
  466.  * least recently used item in the cache.
  467.  */
  468.  
  469. static _kernel_oserror *xFiles_CacheEnsure(xFiles_info *pInfo, unsigned offset, int *pItem, BOOL noRead)
  470. {
  471.    int item;
  472.    unsigned owner = (unsigned) pInfo;
  473.    _kernel_oserror *err;
  474.    xFiles_cacheItem tmp;
  475.  
  476.    ASSERT(offset == roundDown(offset));
  477.    ASSERT(cacheUsed <= cacheSize);
  478.  
  479.    if (item = xFiles_CacheLookup(pInfo, offset), item != -1)
  480.    {
  481.       /*TRACE("%p, %08x -- cache hit\n", pInfo, offset);*/
  482.       xFiles_RemoveCacheItem(item);
  483.       xFiles_CacheAddAtHead(item);
  484.       *pItem = item;
  485.       return NULL;
  486.    }
  487.  
  488.    /*TRACE("%p, %08x -- cache miss\n", pInfo, offset);*/
  489.  
  490.    /* If the cache is full get rid of a chunk. What actually happens is that the chunk to
  491.     * be discarded is moved up to the end of the cache. We can't actually throw it away
  492.     * because each chunk refers to its own bit of buffer and that only gets allocated once.
  493.     */
  494.  
  495.    if (cacheUsed == cacheSize)
  496.    {
  497.       item = cacheTail;
  498.  
  499.       ASSERT(cacheTail != -1);
  500.  
  501.       if (cache[item].dirty)
  502.       {
  503.          if (cache[item].offset != 0xFFFFFFFF)
  504.          {
  505.             /*TRACE("Flush(1) %08x, %08x, %08x\n",
  506.                          cache[item].owner, cache[item].buffer, cache[item].offset);*/
  507.    
  508.             if (err = xFiles_rawWrite((xFiles_info *) cache[item].owner,
  509.                                       cache[item].buffer, cache[item].offset,
  510.                                       xFiles_CACHECHUNKSIZE), err)
  511.                return err;
  512.          }
  513.          
  514.          cache[item].dirty = FALSE;
  515.       }
  516.  
  517.       tmp = cache[item];  /* save it */
  518.  
  519.       /*TRACE("  discarding item %d (%08x, %08x)\n",
  520.                item, cache[item].owner, cache[item].offset);*/
  521.  
  522.       xFiles_RemoveCacheItem(item);  /* remove from the linked list */
  523.       cacheUsed--;
  524.       memmove(&cache[item], &cache[item+1], sizeof(xFiles_cacheItem) * (cacheUsed - item));
  525.       xFiles_AdjustCache(item, -1);
  526.       cache[cacheUsed] = tmp;
  527.    }
  528.  
  529.    for (item = 0; item < cacheUsed && xFiles_CacheCmp(owner, offset, item) > 0; item++)
  530.       ;
  531.  
  532.    /*TRACE("  inserting as item %d\n", item);*/
  533.  
  534.    tmp = cache[cacheUsed];   /* Get the unused one at the end */
  535.    if (item < cacheUsed)
  536.       memmove(&cache[item+1], &cache[item], sizeof(xFiles_cacheItem) * (cacheUsed - item));
  537.    cacheUsed++;
  538.    xFiles_AdjustCache(item, 1);
  539.    cache[item] = tmp;
  540.    xFiles_CacheAddAtHead(item);
  541.    cache[item].owner = owner;
  542.    cache[item].offset = offset;
  543.    cache[item].dirty = FALSE;
  544.  
  545.    if (!(item <= 0 || xFiles_CacheCmp(cache[item-1].owner, cache[item-1].offset, item) < 0) ||
  546.        !(item >= cacheUsed-1 || xFiles_CacheCmp(cache[item+1].owner, cache[item+1].offset, item) > 0))
  547.    {
  548.       unsigned i;
  549.  
  550.       for (i = 0; i < cacheUsed; i++)
  551.       {
  552.          TRACE("%3d: %08x, %08x, %3d, %3d%s\n", i, cache[item].owner, cache[item].offset,
  553.                                                    cache[item].prev, cache[item].next,
  554.                                                    i == item ? " *" : "");
  555.       }
  556.  
  557.       TRACE("xFiles_CacheEnsure(%p, %08x, %p, %d)\n", pInfo, offset, pItem, noRead);
  558.       TRACE("Destroying cache\n");
  559.  
  560.       cacheUsed = 1;    /* just destroy it */
  561.       cacheHead = cacheTail = 0;
  562.       cache[0].prev = cache[0].next = -1;
  563.       item = 0;
  564.    }
  565.  
  566.    *pItem = item;
  567.  
  568.    if (noRead)
  569.       return NULL;
  570.  
  571.    return xFiles_rawRead(pInfo, cache[item].buffer, offset, xFiles_CACHECHUNKSIZE);
  572. }
  573.  
  574. _kernel_oserror *xFiles_read(xFiles_info *pInfo, void *pBuffer, unsigned pos, unsigned size)
  575. {
  576.  
  577.    _kernel_oserror *err;
  578.    int item;
  579.    unsigned start, end;
  580.    unsigned bufPos;
  581.    char *outPtr;
  582.    unsigned fragSize, totalSize;
  583.    unsigned pendingStart, pendingSize;
  584.  
  585.  
  586. #ifndef NOCACHE
  587.    if (!cache)
  588. #endif
  589.       return xFiles_rawRead(pInfo, pBuffer, pos, size);
  590.  
  591.    xFiles_CacheCheck();
  592.  
  593.    /*TRACE("xFiles_read(%p, %p, %08x, %08x)\n", pInfo, pBuffer, pos, size);*/
  594.  
  595.    if (size == 0)
  596.       return NULL;
  597.  
  598.    /* Set about reading it through the cache */
  599.  
  600.    start  = roundDown(pos);
  601.    end    = roundUp(pos + size);
  602.    bufPos = pos - start;
  603.    outPtr = (char *) pBuffer;
  604.    totalSize = size;
  605.  
  606.    ASSERT(start < end);
  607.    ASSERT(bufPos < xFiles_CACHECHUNKSIZE);
  608.  
  609.    if (size > xFiles_OK2CACHE)
  610.    {
  611.       /* Large transfer: check each component block for a cache hit */
  612.  
  613.       pendingStart = pos;
  614.       pendingSize  = 0;
  615.  
  616.       while (start < end)
  617.       {
  618.          item = xFiles_CacheLookup(pInfo, start);
  619.    
  620.          fragSize = _min(xFiles_CACHECHUNKSIZE - bufPos, totalSize);
  621.    
  622.          if (item != -1)
  623.          {
  624.             if (pendingSize != 0)
  625.             {
  626.                if (err = xFiles_rawRead(pInfo, outPtr, pendingStart, pendingSize), err)
  627.                   return err;
  628.  
  629.                pendingStart += pendingSize;
  630.                outPtr += pendingSize;
  631.                pendingSize = 0;
  632.             }
  633.  
  634.             ASSERT(cache[item].buffer >= buffer);
  635.             ASSERT(cache[item].buffer < buffer + xFiles_CACHECHUNKSIZE * cacheSize);
  636.             memcpy(outPtr, cache[item].buffer + bufPos, fragSize);
  637.             outPtr += fragSize;
  638.             pendingStart += fragSize;
  639.          }
  640.          else
  641.             pendingSize += fragSize;
  642.    
  643.          totalSize -= fragSize;
  644.          start     += xFiles_CACHECHUNKSIZE;
  645.          bufPos = 0;
  646.       }
  647.  
  648.       if (pendingSize != 0)
  649.       {
  650.          if (err = xFiles_rawRead(pInfo, outPtr, pendingStart, pendingSize), err)
  651.             return err;
  652.       }
  653.    }
  654.    else
  655.    {
  656.       while (start < end)
  657.       {
  658.          /*TRACE("   reading chunk at %08x through cache\n", start);*/
  659.    
  660.          if (err = xFiles_CacheEnsure(pInfo, start, &item, FALSE), err)
  661.             return err;
  662.    
  663.          fragSize = _min(xFiles_CACHECHUNKSIZE - bufPos, totalSize);
  664.    
  665.          ASSERT(outPtr >= (char *) pBuffer);
  666.          ASSERT(outPtr + fragSize <= (char *) pBuffer + size);
  667.    
  668.          memcpy(outPtr, cache[item].buffer + bufPos, fragSize);
  669.    
  670.          outPtr += fragSize;
  671.          totalSize -= fragSize;
  672.          start  += xFiles_CACHECHUNKSIZE;
  673.          bufPos = 0;
  674.      }
  675.    }
  676.  
  677. #ifndef NOWRITEBEHIND
  678.     xFiles__timer = 200;
  679. #endif
  680.  
  681.    return NULL;
  682. }
  683.  
  684. _kernel_oserror *xFiles_write(xFiles_info *pInfo, void *pBuffer, unsigned pos, unsigned size)
  685. {
  686.    int item;
  687.    unsigned start, end;
  688.    unsigned bufPos;
  689.    char *outPtr;
  690.    unsigned fragSize;
  691.    unsigned totalSize;
  692.    unsigned fileSize;
  693.    BOOL noRead;
  694.    _kernel_oserror *err;
  695.    unsigned pendingStart, pendingSize;
  696.  
  697.    xFiles_CacheCheck();
  698.  
  699.  
  700.    /*TRACE("xFiles_write(%p, %p, %08x, %08x)\n", pInfo, pBuffer, pos, size);*/
  701.  
  702.    if (size == 0)
  703.       return NULL;
  704.  
  705. #ifndef NOCACHE
  706.    if (!cache)
  707. #endif   
  708.       return xFiles_rawWrite(pInfo, pBuffer, pos, size);
  709.  
  710.    start  = roundDown(pos);
  711.    end    = roundUp(pos + size);
  712.    bufPos = pos - start;
  713.    outPtr = (char *) pBuffer;
  714.    totalSize = size;
  715.    ASSERT(start < end);
  716.    ASSERT(bufPos < xFiles_CACHECHUNKSIZE);
  717.  
  718.    if (size > xFiles_OK2CACHE)
  719.    {
  720.       /* Large transfer: check each component block for a cache hit and update
  721.        * the cache too if necessary (bus snooping).
  722.        */
  723.  
  724.       pendingStart = pos;
  725.       pendingSize  = 0;
  726.  
  727.       while (start < end)
  728.       {
  729.          item = xFiles_CacheLookup(pInfo, start);
  730.    
  731.          fragSize = _min(xFiles_CACHECHUNKSIZE - bufPos, totalSize);
  732.    
  733.          if (item != -1)
  734.          {
  735.             if (pendingSize != 0)
  736.             {
  737.                if (err = xFiles_rawWrite(pInfo, outPtr, pendingStart, pendingSize), err)
  738.                   return err;
  739.  
  740.                pendingStart += pendingSize;
  741.                outPtr += pendingSize;
  742.                pendingSize = 0;
  743.             }
  744.  
  745.             ASSERT(cache[item].buffer >= buffer);
  746.             ASSERT(cache[item].buffer < buffer + xFiles_CACHECHUNKSIZE * cacheSize);
  747.             memcpy(cache[item].buffer + bufPos, outPtr, fragSize);
  748.             outPtr += fragSize;
  749.             pendingStart += fragSize;
  750.             cache[item].dirty = TRUE;
  751.          }
  752.          else
  753.             pendingSize += fragSize;
  754.    
  755.          totalSize -= fragSize;
  756.          start     += xFiles_CACHECHUNKSIZE;
  757.          bufPos = 0;
  758.       }
  759.  
  760.       if (pendingSize != 0)
  761.       {
  762.          if (err = xFiles_rawWrite(pInfo, outPtr, pendingStart, pendingSize), err)
  763.             return err;
  764.       }
  765.    }
  766.    else
  767.    {
  768.       if (err = xFiles_getLength(pInfo, &fileSize), err)
  769.          return err;
  770.  
  771.       if (pos + size > fileSize)
  772.       {
  773.          fileSize = xFiles_roundUp(pInfo, pos + size);
  774.          if (err = xFiles_setLength(pInfo, fileSize), err)
  775.             return err;
  776.       }
  777.  
  778.       /* Small transfer: ensure each component block is cached and write to both
  779.        * the cached block and to disc.
  780.        */
  781.  
  782.       while (start < end)
  783.       {
  784.          /* Work out whether we need to read before write */
  785.    
  786.          fragSize = _min(xFiles_CACHECHUNKSIZE - bufPos, totalSize);
  787.  
  788.          noRead = (bufPos == 0 && fragSize == xFiles_CACHECHUNKSIZE);
  789.  
  790.          /*TRACE("   writing chunk at %08x through cache (fragSize = %08x, noRead = %s)\n",
  791.                    start, fragSize, noRead ? "TRUE" : "FALSE");*/
  792.  
  793.          if (err = xFiles_CacheEnsure(pInfo, start, &item, noRead), err)
  794.             return err;
  795.    
  796.          ASSERT(outPtr >= (char *) pBuffer);
  797.          ASSERT(outPtr + fragSize <= (char *) pBuffer + size);
  798.    
  799.          memcpy(cache[item].buffer + bufPos, outPtr, fragSize);
  800.  
  801. #if 1
  802.          cache[item].dirty = TRUE;
  803. #else
  804.          if (err = xFiles_rawWrite(pInfo, cache[item].buffer, cache[item].offset, xFiles_CACHECHUNKSIZE), err)
  805.             return err;
  806. #endif            
  807.  
  808.          outPtr += fragSize;
  809.          totalSize -= fragSize;
  810.          start  += xFiles_CACHECHUNKSIZE;
  811.          bufPos = 0;
  812.       }
  813.  
  814.    }
  815.  
  816. #ifndef NOWRITEBEHIND
  817.     xFiles__timer = 200;
  818. #endif
  819.  
  820.     return NULL;
  821. }
  822.  
  823. _kernel_oserror *xFiles_Commit(xFiles_info *pInfo)
  824. {
  825.    _kernel_oserror *err;
  826.  
  827.    if (err = xFiles_squashFile(pInfo, FALSE), err)
  828.       return err;
  829.  
  830. #ifdef NOWRITEBEHIND
  831.    if (err = xFiles_Flush(), err)
  832.       return err;
  833. #endif   
  834.  
  835.    return NULL;
  836. }
  837.  
  838. /* Force the image file to truncate by
  839.  * issuing the appropriate service call
  840.  */
  841.  
  842. _kernel_oserror *xFiles_Truncate(xFiles_info *pInfo)
  843. {
  844.    _kernel_oserror *err;
  845.    _kernel_swi_regs regs;
  846.    char name[257];
  847.  
  848.    if (pInfo->openList.head != NULL)
  849.    {
  850.       /*TRACE("Don't want to truncate image which has open files\n");*/
  851.       return NULL;
  852.    }
  853.  
  854.    regs.r[0] = 7;
  855.    regs.r[1] = pInfo->fileHandle;
  856.    regs.r[2] = (int) name;
  857.    regs.r[5] = 256;
  858.  
  859.    if (err = _kernel_swi(OS_Args, ®s, ®s), err)
  860.       return err;
  861.  
  862.    /* Now force the named image to close. This will cause X-Files to reenter, so
  863.     * be ready!
  864.     */
  865.  
  866.    /*TRACE("Closing %s\n", name);*/
  867.  
  868.    regs.r[1] = 0x68;
  869.    regs.r[2] = (int) name;
  870.    regs.r[3] = 0;
  871.  
  872.    if (err = _kernel_swi(OS_ServiceCall, ®s, ®s), err)
  873.       return err;
  874.  
  875.    return NULL;
  876. }
  877.  
  878. _kernel_oserror *xFiles_Flush(void)
  879. {
  880.    int i;
  881.    _kernel_oserror *err;
  882.    _kernel_swi_regs regs;
  883.    xFiles_info *pInfo;
  884.  
  885.    for (i = 0; i < cacheUsed; i++)
  886.    {
  887.       if (cache[i].offset != 0xFFFFFFFF && cache[i].dirty)
  888.       {
  889.          /*TRACE("Flush(2) %08x, %08x, %08x\n",
  890.                       cache[i].owner, cache[i].buffer, cache[i].offset);*/
  891.  
  892.          if (err = xFiles_rawWrite((xFiles_info *) cache[i].owner,
  893.                                    cache[i].buffer, cache[i].offset,
  894.                                    xFiles_CACHECHUNKSIZE), err)
  895.             return err;
  896.  
  897.          cache[i].dirty = FALSE;
  898.       }
  899.    }
  900.  
  901.    for (pInfo = (xFiles_info *) xFiles_openFiles.head; pInfo; pInfo = (xFiles_info *) pInfo->li.next)
  902.    {
  903.       regs.r[0] = 255;
  904.       regs.r[1] = pInfo->fileHandle;
  905.    
  906.       if (err = _kernel_swi(OS_Args, ®s, ®s), err)
  907.          return err;
  908.    }
  909.  
  910.  
  911.    return NULL;
  912. }
  913.  
  914. _kernel_oserror *xFiles_WriteBehind(_kernel_swi_regs *regs)
  915. {
  916.    xFiles_info *pInfo, *pNext;
  917.  
  918.    (void) regs;
  919.  
  920. #ifndef NOWRITEBEHIND
  921.  
  922.    (void) xFiles_Flush();
  923.  
  924.    for (pInfo = (xFiles_info *) xFiles_openFiles.head; pInfo; pInfo = pNext)
  925.    {
  926.       pNext = (xFiles_info *) pInfo->li.next;
  927.  
  928.       if (pInfo->flags & xFiles_fNeedTruncate)
  929.       {
  930.          pInfo->flags &= ~xFiles_fNeedTruncate;
  931.          (void) xFiles_Truncate(pInfo);
  932.  
  933.          /* Beware: pInfo might not be valid now */
  934.       }
  935.    }
  936.  
  937. #endif
  938.    
  939.    return NULL;
  940. }
  941.  
  942. extern void entry_WriteBehind(void);
  943.  
  944. #pragma -s1
  945. _kernel_oserror *xFiles_Ticker(_kernel_swi_regs *regs)
  946. {
  947.    (void) regs;
  948.  
  949.    if (xFiles__timer > 0 && --xFiles__timer == 0)
  950.    {
  951.       _kernel_swi_regs regs;
  952.  
  953.       regs.r[0] = (int) entry_WriteBehind;
  954.       regs.r[1] = (int) wsp;
  955.  
  956.       (void) _kernel_swi(OS_AddCallBack, ®s, ®s);
  957.    }
  958.  
  959.    return NULL;
  960. }
  961. #pragma -s0
  962.